<?php

namespace FallingTs\Utils\Traits;

use Closure;
use ReflectionObject;
use ReflectionException;
use Illuminate\Support\Collection;

trait AdvDataTrans
{
    /**
     * @param array $rule
     * @param int|null $i
     * @return array
     * @throws ReflectionException
     */
    public function trans(array $rule, ?int $i = null): array
    {
        $skips = $rule['_skips'] ?? null;
        $dest = [];
        if (is_array($skips)) {
            $dest = collect($this->toArray())->filter(function ($_, $k) use ($skips) {
                return !in_array($k, $skips);
            })->all();
        }

        if ($this instanceof Collection) {
            foreach ($this as $i => $model) {
                $dest[$i] = $model->trans($rule, $i);
            }

            return $dest;
        }

        foreach ($rule as $k => $r) {
            if ($k === '_skips') {
                continue;
            }

            if (is_int($k)) {
                $k = $r;
                if (str_contains($r, ' as ')) {
                    $r = explode(' as ', $r)[0];
                }
                if (str_contains($r, '||')) {
                    $k = explode('||', $r)[0];
                }
            }

            list($modelKey, $destKey) = [$k, $k];
            if (str_contains($k, ' as ')) {
                list($modelKey, $destKey) = explode(' as ', $k);
            }

            if (is_array($r)) {
                if ($this->$modelKey instanceof Collection) {
                    foreach ($this->$modelKey as $i => $relationModel) {
                        $dest[$destKey][$i] = $relationModel->trans($r, $i);
                    }
                } else if (is_object($this->$modelKey) && $this->$modelKey->id) {
                    $dest[$destKey] = $this->$modelKey->trans($r);
                }

            } else if ($r instanceof Closure) {
                $params = (new ReflectionObject($r))
                    ->getMethod('__invoke')
                    ->getParameters();
                if (!count($params)) {
                    $dest[$destKey] = $r();
                    continue;
                }
                $params = collect($params)->map(function ($param) use ($i) {
                    $field = $param->name;
                    if ($field === '_model') {
                        return $this;
                    }
                    if ($field === '_index') {
                        return $i;
                    }
                    return $this->$field;
                })->all();
                $dest[$destKey] = $r(...$params);
            } else {
                $dest[$destKey] = $this->_parseChains(explode('->', $r));
            }
        }
        return $dest;
    }

    /**
     * @param array $chains
     * @return mixed
     */
    protected function _parseChains(array $chains): mixed
    {
        $chain = array_shift($chains);
        $chainWithDefault = explode('||', $chain);
        $chain = $chainWithDefault[0];

        $relation = $this->$chain;
        if ($chains) {
            return $relation->_parseChains($chains);
        }

        if ($relation === null) {
            $default = $chainWithDefault[1] ?? null;
            if ($default === '[]') {
                return [];
            }
            if ($default !== null) {
                return $default;
            }
            return $chain;
        }

        return $relation;
    }
}